﻿using System;
using System.Linq;
using System.Linq.Expressions;
using COFE.Core;
using COFE.Core.Filter;
using COFE.Core.Utils;
using COFE.Data.Utils;

namespace COFE.Data
{
    public class DBEntryListRegistrar : IEntryListRegistrar
    {

        #region Constructor

        #endregion

        #region Methods





        private IEntryList getEntryList(Expression<Func<EntryList, bool>> expr)
        {            
            return COFEDBShared.DBOperations.ReadDatabase(
                (context) => context.EntryLists.FirstOrDefault(expr)
                            as COFE.Data.EntryList,
                (context, dbEntryList) =>
                {              
                    if (dbEntryList is COFE.Data.CustomEntryList)
                        return (IEntryList)new DBCustomEntryList(context,
                            dbEntryList as COFE.Data.CustomEntryList);

                    //if (dbEntryList is COFE.Data.AutoEntryList)
                    //    return (IEntryList)new DBAutoEntryList(context,
                    //        dbEntryList as COFE.Data.AutoEntryList);


                    if (dbEntryList is COFE.Data.AutoEntryList)
                    {
                        var dbAutoEntryList = dbEntryList as COFE.Data.AutoEntryList;
                        dbAutoEntryList.EntryFilters.Load();
                        dbAutoEntryList.OptionFilters.Load();

                        var entryFilters = (from dbF in dbAutoEntryList.EntryFilters
                                        select DBFilterUtils.DB2Filter(context, dbF)).ToArray();
                        var optionFilters = (from dbF in dbAutoEntryList.OptionFilters
                                         select DBFilterUtils.DB2Filter(context, dbF)).ToArray();


                        return (IEntryList)new COFE.Core.AutoEntryList(dbEntryList.EntryListId,
                            dbEntryList.Name, entryFilters, optionFilters);                            
                    }

                    return null;
                });            
        }

        private IEntryList getEntryList(Guid id)
        {
            return getEntryList((el) => el.EntryListId == id);
        }

        private IEntryList getEntryList(string name)
        {
            checkParam(name);
            return getEntryList((el) => el.OwnerId == COFEShared.CurrentUserProfile.UserName && el.Name == name);
        }



        public void updateOrRegisterAutoEntryList(IAutoEntryList autoEntryList)
        {
            COFEDBShared.DBOperations.UpdateDatabase(
                (context) => context.EntryLists
                    .FirstOrDefault(el => 
                        el.OwnerId == COFEShared.CurrentUserProfile.UserName
                        && el.Name == autoEntryList.Name),
                (context, el) =>
                {
                    if (el != null && (
                        !(el is COFE.Data.AutoEntryList) ||
                        el.EntryListId != autoEntryList.Id 
                        ))
                    {
                        //Different type or Id, delete it from db.
                        context.EntryLists.DeleteObject(el);
                        context.SaveChanges();
                        el = null;
                    }

                    COFE.Data.AutoEntryList ael;

                    if (el == null) //Not found, or removed.
                    {
                        ael = AutoEntryList.CreateAutoEntryList(autoEntryList.Id,
                            COFEShared.CurrentUserProfile.UserName,
                            autoEntryList.Name, COFEDBShared.DTMin);
                        context.EntryLists.AddObject(ael);
                        ael.Status = (int)StatusType.Initalized;
                    }
                    else
                    {
                        ael = el as COFE.Data.AutoEntryList;
                        ael.EntryFilters.Load();
                        ael.OptionFilters.Load();
                        ael.Status = (int)autoEntryList.Status;
                        ael.Name = autoEntryList.Name;                        
                        ael.LastUpdateTime = DateTime.UtcNow;
                    }

                    
                    var dbEntryFilters = ael.EntryFilters.ToList();
                    var dbOptionFilters = ael.OptionFilters.ToList();

                    #region Remove removed filter from db.

                    foreach (var dbFilter in dbEntryFilters)
                    {
                        if (!(autoEntryList.EntryFilters.Any(
                            f => f.GetId() == dbFilter.EntryFilterId)))
                            ael.EntryFilters.Remove(dbFilter);
                    }

                    foreach (var dbFilter in dbOptionFilters)
                    {
                        if (!(autoEntryList.OptionFilters.Any(
                            f => f.GetId() == dbFilter.OptionFilterId)))
                            ael.OptionFilters.Remove(dbFilter);
                    }
                    #endregion

                    #region Add new filter to db
                    foreach (var filter in autoEntryList.EntryFilters)
                    {
                        var fid = filter.GetId();
                        if (!(ael.EntryFilters.Any(df => df.EntryFilterId == fid)))
                            ael.EntryFilters.Add(
                                DBFilterUtils.Filter2DB(context, filter));
                    }

                    foreach (var filter in autoEntryList.OptionFilters)
                    {
                        var fid = filter.GetId();
                        if (!(ael.OptionFilters.Any(df => df.OptionFilterId == fid)))
                            ael.OptionFilters.Add(
                                DBFilterUtils.Filter2DB(context, filter));
                    }

                    #endregion

                    context.SaveChanges();

                    //if (!(autoEntryList is DBAutoEntryList))
                    //{
                    //    //updateEntries(context, ael, autoEntryList);
                    //    CacheAutoEntryListWork.ScheduleWork(autoEntryList);
                    //    //context.SaveChanges();
                    //}
                });

        }

        public void updateOrRegisterCustomEntryList(ICustomEntryList customEntryList)
        {
            COFEDBShared.DBOperations.UpdateDatabase(
                (context) => context.EntryLists
                    .FirstOrDefault(el =>
                        el.OwnerId == COFEShared.CurrentUserProfile.UserName
                        && el.Name == customEntryList.Name),
                (context, el) =>
                {
                    if (el != null && (
                         !(el is COFE.Data.CustomEntryList) ||
                         el.EntryListId != customEntryList.Id
                         ))
                    {
                        //Different type, delete it from db.
                        context.EntryLists.DeleteObject(el);
                        context.SaveChanges();
                        el = null;
                    }

                    COFE.Data.CustomEntryList cel;

                    if (el == null) //Not found, or removed.
                    {
                        cel = CustomEntryList.CreateCustomEntryList(
                            customEntryList.Id,
                            COFEShared.CurrentUserProfile.UserName,
                            customEntryList.Name, DateTime.UtcNow);
                        context.EntryLists.AddObject(cel);
                    }
                    else
                    {
                        cel = el as COFE.Data.CustomEntryList;
                        cel.Contents.Load();
                        cel.LastUpdateTime = DateTime.UtcNow;
                    }
                    context.SaveChanges();

                    if (!(customEntryList is DBCustomEntryList))
                    {
                        updateEntries(context, cel, customEntryList);
                        context.SaveChanges();
                    }
                });
        }


        public void updateEntries(COFEDB2 context, COFE.Data.CustomEntryList dbEntryList,
            IEntryList entryList)
        {
            dbEntryList.Contents.Load();

            foreach (var dbEntryContent in dbEntryList.Contents)
            {
                if (!(entryList.Entries.Any(e => e.GetId() == dbEntryContent.EntryId)))
                    dbEntryList.Contents.Remove(dbEntryContent);
            }

            int position = 0;
            foreach (var entry in entryList.Entries)
            {
                Guid entryId = entry.GetId();
                var dbEntryContents = dbEntryList.Contents
                    .FirstOrDefault(e => e.EntryId == entryId);
                if (dbEntryContents == null)
                {
                    if (!(context.Entries.Any(e => e.EntryId == entryId)))
                        throw new ArgumentException();
                    dbEntryContents = COFE.Data.CustomEntryList_Contents
                        .CreateCustomEntryList_Contents(dbEntryList.EntryListId, entryId);
                    dbEntryList.Contents.Add(dbEntryContents);
                }

                dbEntryContents.Position = position;
                position++;
            }
        }

        public void RegisterEntryList(IEntryList entryList)
        {
            if (entryList is IAutoEntryList)
                updateOrRegisterAutoEntryList(entryList as COFE.Core.AutoEntryList);
            else if (entryList is ICustomEntryList)
                updateOrRegisterCustomEntryList(entryList as COFE.Core.CustomEntryList);
            else throw new NotSupportedException("Not AutoEntryList or CustomEntryList.");
        }

        private void RemoveEntryList(COFEDB2 context, Expression<Func<EntryList, bool>> expr)
        {
            if (this.getEntryList(expr) == null)
                return;

            EntryList orgList = context.EntryLists.FirstOrDefault(expr);

            if (orgList != null)
            {
                if (orgList is CustomEntryList)
                    (orgList as CustomEntryList).Contents.Load();

                context.EntryLists.DeleteObject(orgList);
                context.SaveChanges();
            }
        }

        private void checkParam(string name)
        {
            if (String.IsNullOrEmpty(name))
                throw new Exception("Name not specified.");
        }

        public void RemoveEntryList(string name)
        {
            checkParam(name);
            COFEDBShared.DBOperations.UpdateDatabase((context) =>
            RemoveEntryList(context,
            (el) => el.Name == name && el.OwnerId == COFEShared.CurrentUserProfile.UserName));
        }

        public void RemoveEntryList(Guid id)
        {
            COFEDBShared.DBOperations.UpdateDatabase((context) =>
            RemoveEntryList(context, (el) => el.EntryListId == id));
        }


        private User getUser(IUserProfile owner, bool loadEntryList = true)
        {
            return COFEDBShared.DBOperations.ReadDatabase((context) =>
                {
                    User user = context.Users.FirstOrDefault((u) => u.UserId == owner.UserName);
                    if (loadEntryList && user != null)
                        user.EntryLists.Load();
                    return user;
                });
        }



        public string[] GetEntryLists()
        {

            return (from el in getUser(COFEShared.CurrentUserProfile, true).EntryLists select el.Name).ToArray();
        }

        public IEntryList this[string name]
        {
            get { return getEntryList(name); }
        }

        #endregion

        #region Data

        #endregion

        #region Public Properties

        #endregion


    }
}
